package proxy

import (
	myErr "cogpt/internal/cogpterror"
	"golang.org/x/net/proxy"
	"net/http"
	"net/url"
	"os"
	"regexp"
	"time"
)

// @description Get proxy address from the environment
//
// @return string "The proxy address"
func GetEnvProxyAddress() string {
	// possible environment variables: HTTP_PROXY, http_proxy, HTTPS_PROXY, https_proxy, ALL_PROXY, all_proxy
	envVars := []string{"ALL_PROXY", "all_proxy", "HTTPS_PROXY", "https_proxy", "HTTP_PROXY", "http_proxy"}
	for _, envVar := range envVars {
		if proxyAddress := os.Getenv(envVar); proxyAddress != "" {
			return proxyAddress
		}
	}
	return ""
}

// @description Create a transport
//
// @param proxyAddress string "The address of the proxy"
//
// @return (*http.Transport, error) "The transport and the error"
func genTransport(proxyAddress string) (*http.Transport, myErr.Error) {
	// check validitiy of the proxy address
	if proxyAddress != "" {
		if !regexp.MustCompile(`^(http|https|socks5)://(([\w\.-]+)|(\[[\w:]+\]))(:\d+)$`).MatchString(proxyAddress) {
			return nil, &myErr.CoErr{
				ErrCode: myErr.ERR_INVALID_PROXY_ADDRESS,
				Msg:     "Invalid proxy address",
				Typ:     "GenProxyClient",
			}
		}
	}

	// parse the proxy address
	proxyURL, err := url.Parse(proxyAddress)
	if err != nil {
		return nil, &myErr.CoErr{
			ErrCode: myErr.ERR_UNKNOWN,
			Msg:     err.Error(),
			Typ:     "GenProxyClient",
		}
	}

	// create the transport
	switch proxyURL.Scheme {
	case "http", "https":
		return &http.Transport{
			Proxy: http.ProxyURL(proxyURL),
		}, nil
	case "socks5":
		dialer, err := proxy.SOCKS5("tcp", proxyURL.Host, nil, proxy.Direct)
		if err != nil {
			return nil, &myErr.CoErr{
				ErrCode: myErr.ERR_CREATE_PROXY_DIALER,
				Msg:     err.Error(),
				Typ:     "GenProxyClient",
			}
		}
		return &http.Transport{
			Dial:              dialer.Dial,
			ForceAttemptHTTP2: true,
		}, nil
	default:
		return &http.Transport{}, nil
	}
}

// @description Create a proxy client
//
// @param proxyAddress string "The address of the proxy"
//
// @return (*http.Client, error) "The proxy client and the error"
func GenProxyClient(proxyAddress string, timeout float64) (*http.Client, myErr.Error) {
	// get the proxy address
	if proxyAddress == "" {
		proxyAddress = GetEnvProxyAddress()
	}
	// create the transport
	transport, err := genTransport(proxyAddress)
	if err != nil {
		return nil, err
	}

	// create the proxy client
	return &http.Client{
		Timeout:   time.Duration(timeout) * time.Second,
		Transport: transport,
	}, nil
}
